package czy.demo.endpoint;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;

/**
 * @ServerEndpoint标识了一个websocket端点
 * Endpoint接口可以标识这是一个端点Bean，即包含处理连接到端点的事件处理方法，注意这是javax扩展实现
 * 如果使用Endpoint，则无法监听onMessage
 * WebSocketHandler为spring的websocket端点接口，不止包含处理连接的事件方法，还包含了处理消息的方法
 * 如果不继承接口，可以使用注解来标识处理方法
 */
@Component
@ServerEndpoint("/user")
public class UserEndpoint{

    private static final Logger logger = LoggerFactory.getLogger(UserEndpoint.class);

    /* 由于Bean是每个用户一个，所以需要一个线程安全的Map来存储当前所有连接Session */
    private static Map<String, Session> sessions = new ConcurrentHashMap<>();

    /* 当前连接Session */
    private Session localSession;

    /**
     * 只是观察端点的方法，可能会以为，这个端点Bean是全局单例的
     * 然后每进入一个Session，创建一个线程，调用一次单例Bean的onOpen方法
     * 实际上不是的，这个Bean一开始被初始化一次，然后每次连接都会初始化一次
     */
    public UserEndpoint() {
        super();
        logger.info("构造一个websocket端点，可以测试是否为每个连接创建一个Bean");
    }


    /**
     * websocket连接打开调用的方法，每个Bean只调用一次，新的连接会创建新的Bean
     * Session的id从0开始，依次递增
     * @param session：回话，代表了当前客户端的连接
     * @param config
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        logger.info("session打开：："+session.getId());
        this.localSession = session;
        sessions.put(session.getId(),session);
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        logger.info("session关闭："+session.getId());
        this.localSession = null;
        sessions.remove(session.getId());
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        logger.info("session报错："+session.getId());
        throwable.printStackTrace();
    }

    /* 端点接收到消息，将它发送给当前连接端点的所有用户
     * 注意这里的Session，即localSession
     * 注意：客户端发送消息，只有这个客户端对应的Bean的onMessage方法会被调用
     */
    @OnMessage
    public void onMessage(String message, Session session){
        logger.info("Session "+this.localSession.getId()+"接收到消息");
        logger.info("消息为："+message);
        sessions.forEach(new BiConsumer<String, Session>() {
            @Override
            public void accept(String s, Session session) {
                try{
                    logger.info("向"+session.getId()+"发送消息");
                    session.getBasicRemote().sendText("用户"+localSession.getId()+": "+message);
                }catch(IOException ex){
                    ex.printStackTrace();
                }
            }
        });
    }

    public void sendMessage(String message)throws IOException {
        this.localSession.getBasicRemote().sendText(message);
    }

}
